home *** CD-ROM | disk | FTP | other *** search
Wrap
#import <stdio.h> #import <string.h> #import <jinclude.h> #import <streams/streams.h> #import <appkit/graphics.h> #import <appkit/NXBitmapImageRep.h> #import <setjmp.h> #import "NXBitmapImageRepControl.h" #import "ImageControl.h" #import "jpeg.h" @implementation JPEG /* These static variables are needed by the error routines. */ static jmp_buf setjmp_buffer; // for return to caller static external_methods_ptr emethods; // needed for access to message_parm static NXStream *inStream; static NXStream *outStream; static JSAMPARRAY color_map; /* saves color map passed by quantizer */ static NXBitmapImageRep *outputImage; static NXBitmapImageRep *inputImage; static ImageControl *controls; static GetPixelNextFunc nextPixelFunc; static ToGrayFunc toGrayFunc; static unsigned char *outData[5]; static long offset = 0; static int curError; static int errState; METHODDEF void trace_message (const char *msgtext) { fprintf(stderr, msgtext, emethods->message_parm[0], emethods->message_parm[1], emethods->message_parm[2], emethods->message_parm[3], emethods->message_parm[4], emethods->message_parm[5], emethods->message_parm[6], emethods->message_parm[7]); fprintf(stderr, "\n"); /* there is no \n in the format string! */ } METHODDEF void error_exit (const char *msgtext) { trace_message(msgtext); /* report the error message */ (*emethods->free_all) (); /* clean up memory allocation & temp files */ longjmp(setjmp_buffer, 1); /* return control to outer routine */ } METHODDEF void output_init (decompress_info_ptr cinfo) { int spp = 1; NXColorSpace cs = NX_OneIsWhiteColorSpace; if (cinfo->out_color_space == CS_GRAYSCALE) { spp = 1; cs = NX_OneIsWhiteColorSpace; } else if (cinfo->out_color_space == CS_RGB) { spp = 3; cs = NX_RGBColorSpace; } else if (cinfo->out_color_space == CS_CMYK) { spp = 4; cs = NX_CMYKColorSpace; } outputImage = [NXBitmapImageRep alloc]; [outputImage initData: NULL pixelsWide: cinfo->image_width pixelsHigh: cinfo->image_height bitsPerSample: 8 samplesPerPixel: spp hasAlpha: NO isPlanar: YES colorSpace: cs bytesPerRow: 0 bitsPerPixel: 0]; [outputImage getDataPlanes: outData]; } METHODDEF void put_pixel_rows (decompress_info_ptr cinfo, int num_rows, JSAMPIMAGE pixel_data) { register long width = cinfo->image_width; register int row; if (cinfo->out_color_space == CS_GRAYSCALE) { for (row = 0; row < num_rows; row++) { bcopy(pixel_data[0][row], outData[0] + offset, width); offset += width; } } else if (cinfo->out_color_space == CS_RGB) { for (row = 0; row < num_rows; row++) { bcopy(pixel_data[0][row], outData[0] + offset, width); bcopy(pixel_data[1][row], outData[1] + offset, width); bcopy(pixel_data[2][row], outData[2] + offset, width); offset += width; } } else if (cinfo->out_color_space == CS_CMYK) { for (row = 0; row < num_rows; row++) { bcopy(pixel_data[0][row], outData[0] + offset, width); bcopy(pixel_data[1][row], outData[1] + offset, width); bcopy(pixel_data[2][row], outData[2] + offset, width); bcopy(pixel_data[3][row], outData[3] + offset, width); offset += width; } } } METHODDEF void put_color_map (decompress_info_ptr cinfo, int num_colors, JSAMPARRAY colormap) { color_map = colormap; /* save for use in output */ #ifdef DEBUG fprintf(stderr, "This shouldn't get called\n"); #endif // cinfo->methods->put_pixel_rows = put_demapped_rows; } METHODDEF void output_term (decompress_info_ptr cinfo) { #ifdef DEBUG fprintf(stderr, "Image was sucessfully read\n"); #endif errState = CONVERT_ERR_NONE; curError = ERROR_NO_ERROR; } /* * This routine gets control after the input file header has been read. * It must determine what output file format is to be written, * and make any other decompression parameter changes that are desirable. */ METHODDEF void d_ui_method_selection (decompress_info_ptr cinfo) { #ifdef DEBUG fprintf(stderr, "Output format select\n"); #endif cinfo->methods->output_init = output_init; cinfo->methods->put_color_map = put_color_map; cinfo->methods->put_pixel_rows = put_pixel_rows; cinfo->methods->output_term = output_term; } /* * Reload the input buffer after it's been emptied, and return the next byte. * See the JGETC macro for calling conditions. * * This routine would need to be replaced if reading JPEG data from something * other than a stdio stream. */ METHODDEF void progress_monitor (decompress_info_ptr cinfo, long loopcounter, long looplimit) { fprintf(stderr, "%d%%...", (int)((float)loopcounter / (float)looplimit * 100.0)); } METHODDEF int read_jpeg_data (decompress_info_ptr cinfo) { cinfo->next_input_byte = cinfo->input_buffer + MIN_UNGET; cinfo->bytes_in_buffer = NXRead( inStream, cinfo->next_input_byte, JPEG_BUF_SIZE); if (cinfo->bytes_in_buffer <= 0) { curError = ERROR_TRUNCATED_FILE; errState = CONVERT_ERR_WARNING; ERREXIT(cinfo->emethods, "Unexpected EOF in JPEG file"); } return JGETC(cinfo); } - init { return self; } - free { return [super free]; } - readFromStream: (NXStream *)stream from: sender; { struct decompress_info_struct cinfo; struct decompress_methods_struct dc_methods; struct external_methods_struct e_methods; curError = ERROR_NO_ERROR; errState = CONVERT_ERR_NONE; /* Initialize the system-dependent method pointers. */ cinfo.methods = &dc_methods; cinfo.emethods = &e_methods; emethods = &e_methods; // save struct addr for possible access e_methods.error_exit = error_exit; // supply error-exit routine e_methods.trace_message = trace_message; // supply trace-message routine /* prepare setjmp context for possible exit from error_exit */ if (setjmp(setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * Memory allocation has already been cleaned up (see free_all call in * error_exit), but we need to close the input file before returning. * You might also need to close an output file, etc. */ fprintf(stderr, "Bummer, failing\n"); NXClose(outStream); NXClose(inStream); if (errState != CONVERT_ERR_NONE) { curError = ERROR_UNABLE_TO_OPEN; errState = CONVERT_ERR_FATAL; } return outputImage; } jselmemmgr(&e_methods); /* memory allocation routines */ dc_methods.d_ui_method_selection = d_ui_method_selection; j_d_defaults(&cinfo, TRUE); dc_methods.read_jpeg_data = read_jpeg_data; dc_methods. progress_monitor = progress_monitor; /* Set up default input and output file references. */ /* (These may be overridden below.) */ inStream = stream; cinfo.output_file = stderr; outStream = NXOpenFile(fileno(stdout), NX_WRITEONLY); /* Set up default parameters. */ e_methods.trace_level = 0; cinfo.output_gamma = 1.0; cinfo.quantize_colors = FALSE; cinfo.two_pass_quantize = FALSE; cinfo.use_dithering = FALSE; cinfo.desired_number_of_colors = 256; cinfo.do_block_smoothing = FALSE; cinfo.do_pixel_smoothing = FALSE; // cinfo.out_color_space = CS_RGB; cinfo.jpeg_color_space = CS_UNKNOWN; /* setting any other value in jpeg_color_space overrides heuristics */ /* in jrdjfif.c ... */ /* You may wanta change the default output format; here's the place: */ cinfo.do_block_smoothing = FALSE; offset = 0; jselrjfif(&cinfo); /* Do it to it! */ jpeg_decompress(&cinfo); /* Release memory. */ NXClose(outStream); NXClose(inStream); return outputImage; } METHODDEF void progress_monitorII(compress_info_ptr cinfo, long loopcounter, long looplimit) { fprintf(stderr, "%d%%...", (int)((float)loopcounter / (float)looplimit * 100.0)); } METHODDEF void input_init (compress_info_ptr cinfo) /* Initialize for input; return image size and component data. */ { /* This routine must return five pieces of information about the incoming * image, and must do any setup needed for the get_input_row routine. * The image information is returned in fields of the cinfo struct. * (If you don't care about modularity, you could initialize these fields * in the main JPEG calling routine, and make this routine be a no-op.) * We show some example values here. */ cinfo->image_width = [inputImage pixelsWide]; /* width in pixels */ cinfo->image_height = [inputImage pixelsHigh]; /* width in pixels */ /* JPEG views an image as being a rectangular array of pixels, with each * pixel having the same number of "component" values (color channels). * You must specify how many components there are and the colorspace * interpretation of the components. Most applications will use RGB data or * grayscale data. If you want to use something else, you'll need to study * and perhaps modify jcdeflts.c, jccolor.c, and jdcolor.c. */ switch ([inputImage colorSpace]) { case NX_OneIsWhiteColorSpace: cinfo->input_components = 1; cinfo->in_color_space = CS_GRAYSCALE; cinfo->data_precision = 8; break; case NX_OneIsBlackColorSpace: cinfo->input_components = 1; cinfo->in_color_space = CS_GRAYSCALE; cinfo->data_precision = 8; break; case NX_RGBColorSpace: cinfo->input_components = 3; cinfo->in_color_space = CS_RGB; cinfo->data_precision = 8; break; case NX_CMYKColorSpace: cinfo->input_components = 4; cinfo->in_color_space = CS_CMYK; cinfo->data_precision = 8; break; default: fprintf(stderr, "Can't deal with this!\n"); } fprintf(stderr, "%d %d %d\n", cinfo->input_components, cinfo->in_color_space, cinfo->data_precision); } METHODDEF void get_input_row (compress_info_ptr cinfo, JSAMPARRAY pixel_row) /* Read next row of pixels into pixel_row[][] */ { /* This example shows how you might read RGB data (3 components) * from an input file in which the data is stored 3 bytes per pixel * in left-to-right, top-to-bottom order. */ register FILE * infile = cinfo->input_file; register JSAMPROW ptr0, ptr1, ptr2, ptr3; register long col; register Pixel *myPixel; switch ([inputImage colorSpace]) { case NX_OneIsWhiteColorSpace: ptr0 = pixel_row[0]; for (col = 0; col < cinfo->image_width; col++) { *ptr0++ = (JSAMPLE)(255 - nextPixelFunc()->values[0]); } break; case NX_OneIsBlackColorSpace: ptr0 = pixel_row[0]; for (col = 0; col < cinfo->image_width; col++) { *ptr0++ = (JSAMPLE)(nextPixelFunc()->values[0]); } break; case NX_RGBColorSpace: ptr0 = pixel_row[0]; ptr1 = pixel_row[1]; ptr2 = pixel_row[2]; for (col = 0; col < cinfo->image_width; col++) { myPixel = nextPixelFunc(); *ptr0++ = (JSAMPLE)(myPixel->values[0]); *ptr1++ = (JSAMPLE)(myPixel->values[1]); *ptr2++ = (JSAMPLE)(myPixel->values[2]); } break; case NX_CMYKColorSpace: ptr0 = pixel_row[0]; ptr1 = pixel_row[1]; ptr2 = pixel_row[2]; ptr3 = pixel_row[3]; for (col = 0; col < cinfo->image_width; col++) { myPixel = nextPixelFunc(); *ptr0++ = (JSAMPLE)(myPixel->values[0]); *ptr1++ = (JSAMPLE)(myPixel->values[1]); *ptr2++ = (JSAMPLE)(myPixel->values[2]); *ptr3++ = (JSAMPLE)(myPixel->values[3]); } break; default: fprintf(stderr, "Can't deal with this!\n"); } } METHODDEF void input_term (compress_info_ptr cinfo) /* Finish up at the end of the input */ { /* This termination routine will very often have no work to do, */ /* but you must provide it anyway. */ /* Note that the JPEG code will only call it during successful exit; */ /* if you want it called during error exit, you gotta do that yourself. */ } METHODDEF void c_ui_method_selection (compress_info_ptr cinfo) { jselwjfif(cinfo); } - (BOOL)write: (id)image toStream: (NXStream *)stream from: sender; { /* These three structs contain JPEG parameters and working data. * They must survive for the duration of parameter setup and one * call to jpeg_compress; typically, making them local data in the * calling routine is the best strategy. */ struct compress_info_struct cinfo; struct compress_methods_struct c_methods; struct external_methods_struct e_methods; inputImage = image; controls = [sender getImageControl: inputImage]; nextPixelFunc = [controls getNextFunction]; toGrayFunc = [controls getToGrayFunction]; [controls resetNext]; /* Initialize the system-dependent method pointers. */ cinfo.methods = &c_methods; /* links to method structs */ cinfo.emethods = &e_methods; e_methods.error_exit = error_exit; // supply error-exit routine /* prepare setjmp context for possible exit from error_exit */ if (setjmp(setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * Memory allocation has already been cleaned up (see free_all call in * error_exit), but we need to close the input file before returning. * You might also need to close an output file, etc. */ fprintf(stderr, "Bummer, failing\n"); if (errState != CONVERT_ERR_NONE) { curError = ERROR_UNABLE_TO_WRITE; errState = CONVERT_ERR_FATAL; } return NO; } /* Here we use the default JPEG error handler, which will just print * an error message on stderr and call exit(). See the second half of * this file for an example of more graceful error recovery. */ jselerror(&e_methods); /* select std error/trace message routines */ /* Here we use the standard memory manager provided with the JPEG code. * In some cases you might want to replace the memory manager, or at * least the system-dependent part of it, with your own code. */ jselmemmgr(&e_methods); /* select std memory allocation routines */ /* If the compressor requires full-image buffers (for entropy-coding * optimization or a noninterleaved JPEG file), it will create temporary * files for anything that doesn't fit within the maximum-memory setting. * (Note that temp files are NOT needed if you use the default parameters.) * You can change the default maximum-memory setting by changing * e_methods.max_memory_to_use after jselmemmgr returns. * On some systems you may also need to set up a signal handler to * ensure that temporary files are deleted if the program is interrupted. * (This is most important if you are on MS-DOS and use the jmemdos.c * memory manager back end; it will try to grab extended memory for * temp files, and that space will NOT be freed automatically.) * See jcmain.c or jdmain.c for an example signal handler. */ /* Here, set up pointers to your own routines for input data handling * and post-init parameter selection. */ c_methods.input_init = input_init; c_methods.get_input_row = get_input_row; c_methods.input_term = input_term; c_methods.c_ui_method_selection = c_ui_method_selection; /* Set up default JPEG parameters in the cinfo data structure. */ j_c_defaults(&cinfo, 75, FALSE); /* Note: 75 is the recommended default quality level; you may instead pass * a user-specified quality level. Be aware that values below 25 will cause * non-baseline JPEG files to be created (and a warning message to that * effect to be emitted on stderr). This won't bother our decoder, but some * commercial JPEG implementations may choke on non-baseline JPEG files. * If you want to force baseline compatibility, pass TRUE instead of FALSE. * (If non-baseline files are fine, but you could do without that warning * message, set e_methods.trace_level to -1.) */ /* Select the input and output files. * Note that cinfo.input_file is only used if your input reading routines * use it; otherwise, you can just make it NULL. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ c_methods. progress_monitor = progress_monitorII; cinfo.input_file = NULL; /* if no actual input file involved */ cinfo.output_file = (FILE *)stream; /* Here we go! */ jpeg_compress(&cinfo); /* That's it, son. Nothin' else to do, except close files. */ /* Here we assume only the output file need be closed. */ NXFlush(((NXStream *)(cinfo.output_file))); /* Note: if you want to compress more than one image, we recommend you * repeat this whole routine. You MUST repeat the j_c_defaults()/alter * parameters/jpeg_compress() sequence, as some data structures allocated * in j_c_defaults are freed upon exit from jpeg_compress. */ [controls free]; return YES; } - readAllFromStream: (NXStream *)stream from: sender { return nil; } - (BOOL)writeAll: (id)image toStream: (NXStream *)stream { return NO; } - customSaveView: (int)width { return nil; } - customOpenView: (int)width { return nil; } - (char *)getFormatName { return("Stand Alone JPEG Compressed File (JFIF)"); } - (BOOL)setCustomParameter: (const char *)parameter withValue: (void *)ptr { return NO; } - (void *)getCustomParameter: (const char *)parameter { return NULL; } - (char *)copyrightNotice { return "JPEG Converter\nby Alex Raftis\n\nCopyright (c) 1991 Cal Poly State University\nCopyright (c) 1991 The Independent JPEG Group\n\nEmail bugs to alex@data.ACS.CalPoly.EDU"; } - (int)errorState { return errState; } - (int)errorMessage { return curError; } - (char *)errorStringMessage { return NULL; } - (BOOL)needsWindowServer; { return NO; } - (char *)protocolVersion { return "1.0"; } @end